// ==UserScript== // @name Block Specified Websites with Control Panel (Vue & Element UI) // @namespace https://bbs.tampermonkey.net.cn/ // @version 1.7 // @description 使用 Vue 与 Element UI 重构的屏蔽网站脚本,提供可拖动的控制面板来封禁/解除封禁当前网站,并管理封禁列表,同时支持隐藏后以悬浮小球形式显示 // @author // @match *://*/* // @grant GM_getValue // @grant GM_setValue // @run-at document-start // ==/UserScript== (function() { 'use strict'; /* ======================= 工具函数:加载外部资源 ========================= */ function loadScript(url) { return new Promise((resolve, reject) => { const script = document.createElement('script'); script.src = url; script.onload = resolve; script.onerror = reject; document.head.appendChild(script); }); } function loadCSS(url) { return new Promise((resolve, reject) => { const link = document.createElement('link'); link.rel = "stylesheet"; link.href = url; link.onload = resolve; link.onerror = reject; document.head.appendChild(link); }); } /* ======================= 数据存储与基本判断 ========================= */ let blockedSites = GM_getValue('blockedSites', null); if (!blockedSites) { blockedSites = ["youtube.com"]; GM_setValue('blockedSites', blockedSites); } const currentHost = window.location.hostname; function isSiteBlocked(host) { return blockedSites.some(site => host === site || host.endsWith('.' + site)); } /* ======================= 屏蔽页面(覆盖整个 body) ========================= */ function blockPage() { const body = document.body; if (!body) return; body.innerHTML = ''; body.style.margin = '0'; const blockDiv = document.createElement('div'); blockDiv.style.cssText = ` position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: #ff4444; color: white; font-size: 3em; display: flex; justify-content: center; align-items: center; z-index: 99998; cursor: not-allowed; `; blockDiv.innerHTML = '
⚠️ 禁止访问此网站 ⚠️
'; body.appendChild(blockDiv); } if (isSiteBlocked(currentHost)) { if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', blockPage); } else { blockPage(); } // 定时不断重置页面位置和覆盖层 setInterval(() => { window.scrollTo(0, 0); blockPage(); }, 500); } /* ======================= 为 Vue 应用创建容器 ========================= */ function createContainer() { const container = document.createElement('div'); container.id = 'block-control-app'; // 将容器添加到 元素中,以免被 body 覆盖 document.documentElement.appendChild(container); } /* ======================= 加载 Vue 与 Element UI 资源 ========================= */ function loadLibraries() { return Promise.all([ loadCSS('https://unpkg.com/element-ui/lib/theme-chalk/index.css'), loadScript('https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js'), loadScript('https://unpkg.com/element-ui/lib/index.js') ]); } /* ======================= 自定义拖拽指令 用法说明: - 对于需要拖拽的元素,如果希望拖动时移动的是其父元素, 则在指令中传参: v-drag:parent - 否则直接 v-drag 使该元素自身可拖动 ========================= */ function registerDragDirective(Vue) { Vue.directive('drag', { bind(el, binding) { const dragTarget = binding.arg === 'parent' ? el.parentElement : el; el.style.cursor = 'move'; el.onmousedown = function(e) { e.preventDefault(); const disX = e.clientX - dragTarget.offsetLeft; const disY = e.clientY - dragTarget.offsetTop; function onMouseMove(e) { dragTarget.style.left = (e.clientX - disX) + 'px'; dragTarget.style.top = (e.clientY - disY) + 'px'; } function onMouseUp() { document.removeEventListener('mousemove', onMouseMove); document.removeEventListener('mouseup', onMouseUp); } document.addEventListener('mousemove', onMouseMove); document.addEventListener('mouseup', onMouseUp); }; } }); } /* ======================= 初始化 Vue 应用 控制面板采用 Element UI 的卡片、按钮、输入框与折叠面板构造, 同时根据 visible 状态显示或隐藏面板;隐藏时显示一个可拖拽的悬浮小球。 ========================= */ function initVueApp() { new Vue({ el: '#block-control-app', data: { blockedSites: blockedSites, newSite: '', currentHost: currentHost, visible: true // 控制面板是否显示 }, computed: { isBlocked() { return this.blockedSites.some(site => this.currentHost === site || this.currentHost.endsWith('.' + site)); } }, methods: { toggleCurrentSite() { if (this.isBlocked) { this.blockedSites = this.blockedSites.filter(site => !(this.currentHost === site || this.currentHost.endsWith('.' + site))); GM_setValue('blockedSites', this.blockedSites); alert("已解除封禁此网站,请刷新页面。"); location.reload(); } else { this.blockedSites.push(this.currentHost); GM_setValue('blockedSites', this.blockedSites); alert("已封禁此网站,请刷新页面。"); location.reload(); } }, addSite() { const newSiteTrim = this.newSite.trim(); if (newSiteTrim && !this.blockedSites.includes(newSiteTrim)) { this.blockedSites.push(newSiteTrim); GM_setValue('blockedSites', this.blockedSites); this.newSite = ''; } }, removeSite(index) { this.blockedSites.splice(index, 1); GM_setValue('blockedSites', this.blockedSites); location.reload(); }, hidePanel() { this.visible = false; }, showPanel() { this.visible = true; } }, template: `
网站屏蔽面板 隐藏
{{ isBlocked ? '解除封禁此网站' : '封禁此网站' }}
  • {{ site }} 移除
添加
显示
` }); } /* ======================= 主初始化流程 ========================= */ function init() { createContainer(); loadLibraries().then(() => { registerDragDirective(Vue); initVueApp(); }).catch(err => { console.error('加载 Vue 或 Element UI 失败:', err); }); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();